package com.ming.tflitesingleblur.utils.texturerecoder;

import android.content.Context;
import android.opengl.GLES20;
import android.util.Log;

import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.util.HashMap;

public class Shader {
	private int mProgram = 0;
	private int mShaderVertex = 0;
	private int mShaderFragment = 0;

	//hashmap for storing uniform/attribute handles
	private final HashMap<String, Integer> mShaderHandleMap = new HashMap<String, Integer>();
	
	
	public Shader() {
		// TODO Auto-generated constructor stub
	}

	public void setProgram(String vertexSource, String fragmentSource) throws Exception {
		mShaderVertex = loadShader(GLES20.GL_VERTEX_SHADER, vertexSource);
		mShaderFragment = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource);

		int program = GLES20.glCreateProgram();
		if(program != 0){
			GLES20.glAttachShader(program, mShaderVertex);
			GLES20.glAttachShader(program, mShaderFragment);
			GLES20.glLinkProgram(program);
			int[] linkStatus = new int[1];
			GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0);
			if(linkStatus[0] != GLES20.GL_TRUE){
				String error = GLES20.glGetProgramInfoLog(program);
				deleteProgram();
				throw new Exception(error);
			}
		}
		else Log.e("GLSD Shader:","could not create program");

		mProgram = program;
		mShaderHandleMap.clear();

	}
	
	public void setProgram(int vertexShader, int fragmentShader, Context context)
			throws Exception {
		String vertexSource = loadRawString(vertexShader, context);
		String fragmentSource = loadRawString(fragmentShader, context);
		
		setProgram(vertexSource,fragmentSource);
	}
	
	public void useProgram(){
		GLES20.glUseProgram(mProgram);
	}
	
	public void deleteProgram(){
		GLES20.glDeleteShader(mShaderVertex);
		GLES20.glDeleteShader(mShaderFragment);
		GLES20.glDeleteProgram(mProgram);
		mProgram = mShaderVertex = mShaderFragment = 0;
	}
	
	public int programHandle(){
		return mProgram;
	}
	
	public int getHandle(String name){
		if(mShaderHandleMap.containsKey(name)){
			return mShaderHandleMap.get(name);
		}

		int handle = GLES20.glGetAttribLocation(mProgram, name);
		if(handle == -1){
			handle = GLES20.glGetUniformLocation(mProgram, name);
		}

		if(handle == -1){
			Log.d("GLSL shader", "Could not get attrib location for " + name);
		}else{
			mShaderHandleMap.put(name, handle);
		}
		
		return handle;
	}
	
	public int[] getHandles(String... names){
		int[] res = new int[names.length];
		for(int i = 0; i < names.length; ++i){
			res[i] = getHandle(names[i]);
		}
		
		return res;
	}

	private int loadShader(int shaderType, String source) throws Exception {
		int shader = GLES20.glCreateShader(shaderType);
		if(shader != 0){
			GLES20.glShaderSource(shader, source);
			GLES20.glCompileShader(shader);
			int[] compiled = new int[1];
			GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0);

			if(compiled[0] == 0){
				String error = GLES20.glGetShaderInfoLog(shader);
				GLES20.glDeleteShader(shader);

				Log.e("Shader compile error:",source);

				throw new Exception(error);
			}
		}

		return shader;
	}
	
	private String loadRawString(int rawId, Context context) throws Exception {
		InputStream is = context.getResources().openRawResource(rawId);
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		byte[] buf = new byte[1024];
		int len;
		while((len = is.read(buf))!= -1){
			baos.write(buf, 0, len);
		}
		return baos.toString();
	}
	
	
}
